package main

import (
	"errors"
	"fmt"
	"hello/src/code/003about_library/common"
	"time"

	_ "github.com/go-sql-driver/mysql" // 代码不直接使用包, 底层链接要使用, 使用_则会先初始化该包的init()函数
	"github.com/jinzhu/gorm"
)

/* gorm: 对象关系映射 */

// 创建全局的连接池句柄
var GlobalConn *gorm.DB

// 创建表的结构体
// 修改表属性只在第一次建表时生效
type Student struct {
	// Id int // 称为默认的表主键
	gorm.Model
	Name       string `gorm:"size:100;default:'小王子';not null"`
	Age        int    `gorm:"not null"`
	CreateTime int64
}

func InsertData() {
	// 创建对象
	var stu Student
	stu.Name = common.GetFullName()
	stu.Age = common.GenerateRangeNum(0, 100)
	stu.CreateTime = time.Now().Unix()
	// 插入表
	insertRes := GlobalConn.Create(&stu)
	fmt.Println("错误信息error:", insertRes.Error)
	fmt.Println("影响行数RowsAffected:", insertRes.RowsAffected)
}

func SearchData() {
	// First(&user): 获取表中的 第一条 数据
	// 按主键排序, select * order by id limit 1
	var searchStu Student
	GlobalConn.First(&searchStu)
	fmt.Println("First: ", searchStu)
	GlobalConn.First(&searchStu, "code=?", "D42") // 查找code为D42的记录

	// Last(&user): 获取表中的 最后一条 数据
	// select * order by id desc limit 1
	GlobalConn.Last(&searchStu)
	fmt.Println("Last: ", searchStu)

	// Find(&user): 获取表中的 所有 数据
	// select * from user
	GlobalConn.Find(&searchStu)
	fmt.Println("Find: ", searchStu)

	// Select、Where
	var stus []Student // 改为切片, 用于查询所有数据
	GlobalConn.Select("name, age").Find(&stus)
	fmt.Println("Select1: ", stus)
	GlobalConn.Select("name, age").Where("name = ?", "小王子").Find(&stus)
	fmt.Println("Select2: ", stus)
	GlobalConn.Select("name, age").Where("name = ?", "小王子").Where("age = ?", 18).Find(&stus)
	fmt.Println("Select3: ", stus)

	// In
	GlobalConn.Where("name in (?)", []string{"娜扎", "小王子"}).Find(&stus)
	fmt.Println("In: ", stus)

	// Like
	GlobalConn.Where("name LIKE ?", "%小王子%").Find(&stus)
	fmt.Println("Like: ", stus)

	// And
	GlobalConn.Select("name, age").Where("name = ? And age = ?", "小王子", 18).Find(&stus)
	fmt.Println("And: ", stus)

	// Time
	lastweek, today := time.Now().Unix()-168*3600, time.Now().Unix()
	GlobalConn.Where("create_time > ?", lastweek).Find(&stus)
	fmt.Println("Time1: ", stus)
	GlobalConn.Where("create_time between ? And ?", lastweek, today).Find(&stus)
	fmt.Println("Time2: ", stus)
}

func (u *Student) BeforeUpdate(tx *gorm.DB) (err error) {
	fmt.Println("------更新的钩子函数, 每次更新数据都会调用------")
	if u.Name == "小王子" {
		return errors.New("admin user not allowed to update")
	}
	return
}

func (u *Student) BeforeDelete(tx *gorm.DB) (err error) {
	fmt.Println("------删除的钩子函数, 每次删除数据都会调用------")
	if u.Name == "小王子" {
		return errors.New("admin user not allowed to delete")
	}
	return
}

func UpdateWithTableId() {
	// 指定Id更新数据
	var stu Student
	stu.ID = 1
	stu.Name = "王麻子"
	stu.Age = 100
	res := GlobalConn.Save(&stu)
	if res.Error != nil {
		fmt.Println("res.Error:", res.Error)
	}
	fmt.Println(res.RowsAffected)
}

func UpdateData() {
	// 条件更新
	// GlobalConn.Model(&Student{}): 指定更新`Student`表
	// UPDATE student SET name='小七' where name = "柏青";
	// GlobalConn.Model((new(Student))).Where("name = ?", "柏青").Update("name", "小七")
	GlobalConn.Model(&Student{}).Where("name = ?", "柏青").Update("name", "小七")

	// 更新多列
	// 根据 `struct` 更新属性，只会更新非零值的字段
	// GlobalConn.Model((new(Student))).Where("name = ?", "韦荔").Updates(Student{Name: "UpdatesName", Age: 99})
	GlobalConn.Model(&Student{}).Where("name = ?", "韦荔").Updates(Student{Name: "UpdatesName", Age: 99})

	// 更新选定字段, 并获取更细的次数和错误信息
	resp := GlobalConn.Model(&Student{}).Omit("name").Updates(Student{Name: "小王子", Age: 8})
	fmt.Println("更新的记录数", resp.RowsAffected) // 更新的记录数 1
	fmt.Println("更新的错误", resp.Error)         // <nil>

	// 批量更新: 使用sql表达式
	// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = 3 AND quantity > 1;
	GlobalConn.Model(&Student{}).Where("age > 1").UpdateColumn("age", gorm.Expr("age + ?", 10))
}

func DeleteData() {
	// 根据条件删除
	// DELETE from emails where id = 10 AND name = "jinzhu";
	GlobalConn.Where("name = ?", "司徒河").Delete(&Student{})

	// 根据主键删除
	// GlobalConn.Delete(&Student{}, "10")          // DELETE FROM users WHERE id = 10;
	// GlobalConn.Delete(&Student{}, []int{12, 13}) // DELETE FROM users WHERE id IN (1,2,3);

	// 批量删除
	// DELETE from emails where email LIKE "%jinzhu%";
	GlobalConn.Where("name LIKE ?", "%毛飘%").Delete(Student{})

	// 永久删除
	// DELETE FROM orders WHERE id=10;
	// GlobalConn.Unscoped().Delete(&Student{})
}

func main() {
	// 1. 链接数据库，获取游标
	// parseTime: 解决time.Time类型数据转换的问题
	// loc: 解决mysql八小时时区的问题
	conn, err := gorm.Open("mysql", "root:test123456@tcp(127.0.0.1:3306)/golang_gin_test?parseTime=True&loc=Asia%2FShanghai")
	if err != nil {
		fmt.Println("db open failed, err: ", err)
		return
	}
	// defer conn.Close() // 不需要单独关闭游标
	GlobalConn = conn
	GlobalConn.DB().SetMaxIdleConns(10) // 设置最大连接池
	GlobalConn.DB().SetMaxIdleConns(100)

	// 2. 创建数据库表
	// 每次创建表的时候，表名为结构体名字加一个"s", 如果不希望被添加则可以指定SingularTable=true
	GlobalConn.SingularTable(true)
	fmt.Println(GlobalConn.AutoMigrate(new(Student)).Error)

	// 3. 参考地址: https://learnku.com/docs/gorm/v2
	// 3.1 插入数据
	InsertData()

	// 3.2 查询数据
	SearchData()

	// 3.3 更新数据
	UpdateData()

	// 3.4 删除数据
	DeleteData()
}
